#include "RenderTextureGLES.h"
#include "RendererGLES.h"
#include "GraphicsDriver.h"
#include "FrameBufferObject.h"

namespace Nezha
{
	RenderBufferGLES::RenderBufferGLES(HardwareBuffer::Usage usage, u16 w, u16 h, u16 numSamples)
		:HardwareBuffer(usage, false)
		,mRenderBufferHandle(0)
		,mGLFormat(0)
		,mNumSamples(numSamples)
		,mWidth(w)
		,mHeight(h)
	{
	}

	RenderBufferGLES::~RenderBufferGLES()
	{
		glDeleteRenderbuffers(1, &mRenderBufferHandle);
		GL_ERROR_DUMP(GLESAdaptor::GetInstance());
	}

	bool RenderBufferGLES::_initializeOnce(GLenum glFmt, u16 bitDepth)
	{
		if(mRenderBufferHandle != 0)
		{
			return false;
		}

		glGenRenderbuffers(1, &mRenderBufferHandle);	
		GL_ERROR_DUMP(GLESAdaptor::GetInstance());

		glBindRenderbuffer(GL_RENDERBUFFER, mRenderBufferHandle);
		GL_ERROR_DUMP(GLESAdaptor::GetInstance());

		mGLFormat = glFmt;

		switch(mUsage)
		{
		case HardwareBuffer::HBU_COLORBUFFER:
			{
				// FIXME for color buffer, need a variable for choosing gl format.
				if(mNumSamples > 0)
				{
#ifdef NZOS_IOS

#	if	GL_APPLE_framebuffer_multisample
					glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, 
						mNumSamples, mGLFormat, mWidth, mHeight);
					GL_ERROR_DUMP(r->getAdaptor());
#	endif

#endif
				}
				else
				{
					glRenderbufferStorage(GL_RENDERBUFFER, mGLFormat, mWidth, mHeight);
					GL_ERROR_DUMP(GLESAdaptor::GetInstance());
				}
			}
			break;
		case HardwareBuffer::HBU_DEPTH:
		case HardwareBuffer::HBU_STENCIL:
		case HardwareBuffer::HBU_DEPTHSTENCIL:
			{
				glRenderbufferStorage(GL_RENDERBUFFER, mGLFormat, mWidth, mHeight);
				GL_ERROR_DUMP(GLESAdaptor::GetInstance());
			}
			break;
		}

		return true;
	}

	GLenum RenderBufferGLES::Usage2GLFormat(HardwareBuffer::Usage usage, u16 bitDepth)
	{
		GLenum fmt = 0;

		switch(usage)
		{
		case HardwareBuffer::HBU_COLORBUFFER:
			// FIXME for color buffer, need a variable for choosing gl format.
			fmt = GL_RGBA4;
			break;
		case HardwareBuffer::HBU_DEPTH:
			{
				RendererGLES* r = static_cast<RendererGLES*>(Renderer::GetGlobal());
				u16 dfs = r->getDepthFormatSupported();
				fmt = GL_DEPTH_COMPONENT16;

				if(bitDepth == 32)
				{
					if(!(dfs & RendererGLES::DEPTH_32BIT))
					{
						bitDepth = 24;
					}
					else
					{
#if GL_OES_depth32

						fmt = GL_DEPTH_COMPONENT32_OES;

#else
						bitDepth = 24;

#endif
					}
				}

				if(bitDepth == 24)
				{
					if(!(dfs & RendererGLES::DEPTH_24BIT))
					{
						bitDepth = 16;
					}
					else
					{
#if GL_OES_depth24

						fmt = GL_DEPTH_COMPONENT24_OES;

#else
						bitDepth = 16;

#endif
					}
				}
			}
			break;
		case HardwareBuffer::HBU_STENCIL:
			{
				fmt = GL_STENCIL_INDEX8;
			}
			break;
		case HardwareBuffer::HBU_DEPTHSTENCIL:
			{
#if GL_OES_packed_depth_stencil

				RendererGLES* r = static_cast<RendererGLES*>(Renderer::GetGlobal());
				u16 dfs = r->getDepthFormatSupported();

				if(dfs & RendererGLES::DEPTH_PACKED_SEPTH_STENCIL)
				{
					fmt = GL_DEPTH24_STENCIL8_OES
				}

#endif
			}
			break;
		}

		return fmt;
	}

	void* RenderBufferGLES::lockImpl(u32 offset, u32 length, LockOption opt)
	{
		return NULL;
	}

	void RenderBufferGLES::unlockImpl()
	{
	}

	void RenderBufferGLES::bind2FrameBuffer(GLenum attachment)
	{
		glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, mRenderBufferHandle);
		GL_ERROR_DUMP(GLESAdaptor::GetInstance());
	}



	DepthBufferGLES::DepthBufferGLES(RenderBufferGLES* depthBuf, RenderBufferGLES* stencilBuf, u16 w, u16 h, u16 multisamples)
		:DepthStencilBuffer(16, w, h)
		,mPacked(false)
		,mMultisamples(multisamples)
	{
		// TODO determine bitdepth

		mDepthBuffer = depthBuf;
		mStencilBuffer = stencilBuf;

		switch(depthBuf->getGLFormat())
		{
		case GL_DEPTH_COMPONENT16:
			mBitDepth = 16;
			break;
#if GL_OES_depth24
		case GL_DEPTH_COMPONENT24_OES:
#endif
#if GL_OES_depth32
		case GL_DEPTH_COMPONENT32_OES:
#endif
#if GL_OES_packed_depth_stencil
		case GL_DEPTH24_STENCIL8_OES:  // Packed depth / stencil
#endif
			mBitDepth = 32;
			break;
		}

#if GL_OES_packed_depth_stencil
		if(depthBuf->getGLFormat() == GL_DEPTH24_STENCIL8_OES)
			mPacked = true;
#endif
	}

	DepthBufferGLES::~DepthBufferGLES()
	{
		mDepthBuffer.release();
		mStencilBuffer.release();
	}

	bool DepthBufferGLES::isCompatible(RenderTarget* rt) const
	{
		bool ret = false;

		Renderer* r = Renderer::GetGlobal();
		GraphicsDriver* gd = r->getDriver();

		if(gd->hasCapability(GC_RTT_DEPTHBUFFER_LESSEQUAL))
		{
			return DepthStencilBuffer::isCompatible(rt);
		}
		else
		{
			if(rt->getWidth() != mWidth || rt->getHeight() != mHeight)
			{
				return false;
			}
		}

		// check format
		RenderTextureGLES* rt_gles = static_cast<RenderTextureGLES*>(rt);

		// TODO

		return ret;
	}



	RenderTextureGLES::RenderTextureGLES(u16 w, u16 h, u16 numSamples, u16 bitDepth)
		:mFBO(NULL)
	{
		mWidth = w;
		mHeight = h;
		mNumSamples = numSamples;
		mColorDepth = bitDepth;
	}

	bool RenderTextureGLES::_initializeOnce(FBORTTManager* fboMgr, TexFormat fmt, bool renderTexture /* = false */)
	{
		mFBO = NZ_New FrameBufferObject(mNumSamples);

		if(!mFBO->initializeOnce())
		{
			NZ_Delete mFBO;
			mFBO = NULL;

			return false;
		}

		// TODO attach a color buffer
		FBOAttachment colAttachment;
		colAttachment.mNumSamples = mNumSamples;
		colAttachment.mZOffset = 0;

		if(!renderTexture)
		{
			RenderBufferGLESPtr rbptr = fboMgr->createRenderBuffer(HardwareBuffer::HBU_COLORBUFFER, mWidth, mHeight, mNumSamples, mColorDepth);
			GLenum glFmt = GL_RGBA;

			if(fmt == TF_R5G6B5)
				glFmt = GL_RGB565;
			else if(fmt == TF_A1R5G5B5)
				glFmt = GL_RGB5_A1;

			if(!rbptr->_initializeOnce(glFmt, mColorDepth))
			{
				return false;
			}

			colAttachment.mBufferType = FBOAttachment::AT_RENDER_BUFFER;
			colAttachment.mRenderBuffer = rbptr;
		}
		else
		{
			Renderer* r = Renderer::GetGlobal();
			TextureManager* tm = r->getTextureManager();
			_Image2D img2d;
			img2d.mFormat = fmt;
			img2d.mWidth = mWidth;
			img2d.mHeight = mHeight;

			TexturePtr tex = tm->create2DTexture(img2d, false, HardwareBuffer::HBU_COLORBUFFER);

			if(!tex)
			{
				return false;
			}

			colAttachment.mBufferType = FBOAttachment::AT_TEXTURE;
			colAttachment.mTexture = tex;
		}

		mFBO->attachColorBuffer(0, colAttachment);

		return true;
	}

	Texture* RenderTextureGLES::getRenderTexture(u32 index)
	{
		if(mFBO)
		{
			FBOAttachment& fboAtt = mFBO->getColorBuffer(index);
			if(fboAtt.mBufferType == FBOAttachment::AT_TEXTURE)
			{
				return fboAtt.mTexture.get();
			}
		}

		return NULL;
	}

	bool RenderTextureGLES::attachDepthStencilBuffer(DepthStencilBuffer* ds)
	{
		if(mFBO && RenderTarget::attachDepthStencilBuffer(ds))
		{
			DepthBufferGLES* db = static_cast<DepthBufferGLES*>(ds);
			DepthBufferGLESPtr dbp(db);
			if(mFBO->attachDepthBuffer(dbp))
			{
				mDepthStencilBuffer = ds;
				return true;
			}
			else
			{
				mDepthStencilBuffer = NULL;
			}
		}

		return false;
	}

	void RenderTextureGLES::detachDepthStencilBuffer()
	{
		if(mFBO)
		{
			mFBO->detachDepthBuffer();
			mDepthStencilBuffer.release();
		}
	}
}